/************************************
 文件明 ：  start.s
 作者   ：  胡秋峰
 描述   ：  开发板启动代码，完成C环境初始化
            并跳转到C代码    
 ************************************/
 
.global _start /* 全局标号 */

/**************************
  描述  ：  创建中断向量表
 **************************/
_start:
    ldr pc, =Reset_Handler      /* 复位中断 */
    ldr pc, =Undefined_Handler  /* 未定义指令中断 */
    ldr pc, =SVC_Handler        /* SVC(Supervisor)中断 */
    ldr pc, =PrefAbort_Handler  /* 预取终止中断 */
    ldr pc, =DataAbort_Handler  /* 数据终止中断 */
    ldr pc, =NotUsed_Handler    /* 未使用中断 */
    ldr pc, =IRQ_Handler        /* IRQ中断 */
    ldr pc, =FIQ_Handler        /* FIQ中断 */

Reset_Handler:
    cpsid i                     /* 关闭全局中断 */

    /* 关闭I，DCache和MMU
     * 采取读-改-写的方式
     */
    mrc p15, 0, r0, c1, c0, 0   /* 读取CP15的C1寄存器到R0中 */ 
    bic r0, r0, #(0x1 << 12)    /* 清除C1的I位，关闭I Cache */
    bic r0, r0, #(0x1 << 2)     /* 清除C1的C位，关闭D Cache */
    bic r0, r0, #(0x1 << 1)     /* 清除C1的A位，关闭对其检查 */
    bic r0, r0, #(0x1 << 11)    /* 清除C1的Z位，关闭分支预测 */
    bic r0, r0, #(0x1 << 0)     /* 清除C1的M位，关闭MMU */
    mcr p15, 0, r0, c1, c0, 0   /* 将r0值写入到CP15的C1中 */

#if 0
    ldr r0, =0X87800000

    dsb
    isb
    mcr p15, 0, r0, c12, c0, 0
    dsb
    isb
#endif

    /* 设置各个模式下的栈指针
     * 注意：IMX6UL的堆栈是向下增长的
     * 堆栈指针地址一定是4字节对齐的
     * DDR范围：0X80000000~0X9FFFFFFF
     */
    /* 进入IRQ模式 */ 
    mrs r0, cpsr
    bic r0, r0, #0X1F   /* 低五位清零 */
    orr r0, r0, #0X12   /* r0低五位设置为0X12，表示IRQ模式 */
    msr cpsr, r0        /* 将r0写入到cpsr中 */
    ldr sp, =0X80600000 /* IRQ模式站首地址为0X80600000，大小为2MB */

    /* 进入SYS模式 */
    mrs r0, cpsr
    bic r0, r0, #0X1F
    orr r0, r0, #0X1F   /* 表使用SYS模式 */
    msr cpsr, r0
    ldr sp, =0X80400000 /* SYS模式栈首地址为0X80400000，大小为2MB */
    
    /* 进入SYC模式 */
    mrs r0, cpsr
    bic r0, r0, #0X1F
    orr r0, r0, #0X13   /* 表使用SYC模式 */
    msr cpsr, r0
    ldr sp, =0X80200000 /* SYC模式栈首地址为0X80200000，大小为2MB */
    
    cpsie i             /* 打开全局中断 */

#if 0
    /* 使能IRQ中断 */
    mrs r0, cpsr
    bic r0, r0, #(0x1 << 7)   /* cpsr I位清零，表示允许IRQ中断 */
    msr cpsr, r0
#endif

    b main              /* 跳转到main函数 */


Undefined_Handler:
    ldr r0, =Undefined_Handler
    bx r0

SVC_Handler:
    ldr r0, =SVC_Handler
    bx r0

PrefAbort_Handler:
    ldr r0, =PrefAbort_Handler
    bx r0

DataAbort_Handler:
    ldr r0, =DataAbort_Handler
    bx r0

NotUsed_Handler:
    ldr r0, =NotUsed_Handler
    bx r0

IRQ_Handler:
    push {lr}           /* 保存lr地址 */
    push {r0-r3, r12}   /* 保存r0,r1,r2,r3,r12寄存器 */

    mrs r0, spsr        /* 读取spsr寄存器 */
    push {r0}           /* 保存spsr寄存器 */

    mrc p15, 4, r1, c15, c0, 0 /* 将CP15的C0内的值赋值到R1寄存器中 */
    add r1, r1, #0X2000 /* GIC基址加0X2000，得到CPU接口端基址 */
    ldr r0, [r1, #0XC]  /* CPU基址加0XC就是GICC_IAR寄存器，
                         * GICC_IAR保存当前发生中断的中断号 
                         * r0 保存中断号，在调用C语言函数时将r0作为参数传递过去
                         */
    push {r0, r1}

    cps #0X13           /* 进入SVC模式，允许其他中断再次进入 */

    push {lr}           /* 保存SVC模式的lr寄存器 */
    ldr r2, =system_irq_handler /* 加载c语言中断处理函数到r2寄存器 */
    blx r2              /* 运行C语言中断处理函数，带一个参数 */

    pop {lr}            /* 执行完C语言中的中断服务函数，lr出栈 */
    cps #0X12           /* 进入IRQ模式 */
    pop {r0, r1}
    str r0, [r1, #0X10] /* 中断执行完成，将中断号写入写EOIR
                         * 表示中断处理完成 
                         */

    pop {r0}
    msr spsr_cxsf, r0   /* 恢复spsr */

    pop {r0-r3, r12}    /* r0-r3,r12出栈 */
    pop {lr}            /* lr出栈 */
    subs pc, lr, #4     /* 将lr-4赋值给pc */

FIQ_Handler:
    ldr r0, =FIQ_Handler
    bx r0
